package controllers

import config._
import dao._

import javax.inject.Inject
import models.Tables._
import org.apache.commons.io.FileUtils
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}

import scala.collection.mutable
import scala.jdk.CollectionConverters._

class InsertController @Inject()(genomeDao: GenomeDao,
                                 blockDao: BlockDao,
                                 blockRangeDao: BlockRangeDao,
                                 cc: ControllerComponents) extends AbstractController(cc) with MyFile with MyAwait with MyString {


  def insert(insert: String) = Action {
    insert match {
      case "insertGenome" => insertGenome
      case "addGenome" => addGenome
      case "insertCamelliaLanceoleosa" => insertCamelliaLanceoleosa
      case "insertColeifera" => insertColeifera
      case "geteQTL" => geteQTL
      case "insertBlockTable" => insertBlockTable
      case "insertBlockRange" => insertBlockRange
      case "getBlockRange" => getBlockRange
      case "addGenome2" => addGenome2
    }
    Ok(Json.toJson("success"))
  }

  def insertGenome = {
    val path = "H:\\南农油茶数据库\\南荣油茶基因组数据\\CON_genome_v1.0_all.genes.gff"

    val annos = s"$path/annotation.table.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      val geneid = line.head.split('-').dropRight(2).mkString("-")

      geneid -> line
    }.groupBy(_._1).map { x =>
      x._1 -> x._2.map(_._2).maxBy(_(1).toInt)
    }

    val mrnaToGene = annos.map(x => x._2.head -> x._1)

    val pfam = s"$path/anno.pfam.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      mrnaToGene.getOrElse(line.head, "None") -> line(3)
    }.filter(_._1 != "None").toMap


    val row = s"$path/Chrall.genes.gff".readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "gene").map { ele =>
      val geneId = ele.last.split(";").find(_.startsWith("ID=")).get.drop(3)
      val chr = ele.head
      val start = ele(3).toInt
      val end = ele(4).toInt
      val strand = ele(6)
      val anno = annos(geneId)
      val nrName = anno(2)
      val nrDes = anno(3)

      val swissName = anno(5)
      val swissDes = anno(6)

      val go = anno(8)
      val cog = anno(9)
      val ko = anno(11)
      val kegg = anno(12)
      val pfmas = pfam.getOrElse(geneId, "_")
      GenomeRow(0, geneId, "1", chr, start, end, strand, nrName, nrDes, swissName, swissDes, go, cog, ko, kegg, pfmas)
    }.toSeq
    genomeDao.insertGenome(row).toAwait

    println("success")
  }

  def insertCamelliaLanceoleosa = {
    val path = "H:\\南农油茶数据库\\new\\Camellia_lanceoleosa"

    val annos = s"$path/annotation.table.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      val geneid = line.head
      geneid -> line
    }.toMap

    val pfam = s"$path/anno.pfam.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      line.head -> line(3)
    }.toMap


    val row = s"$path/genomic.gff".readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "gene").map { ele =>
      val geneId = ele.last.split(";").find(_.startsWith("Name=")).get.drop(5)
      val chr = ele.head
      val start = ele(3).toInt
      val end = ele(4).toInt
      val strand = ele(6)
      val anno = annos(geneId)
      val nrName = anno(2)
      val nrDes = anno(3)

      val swissName = anno(5)
      val swissDes = anno(6)

      val go = anno(8)
      val cog = anno(9)
      val ko = anno(11)
      val kegg = anno(12)
      val pfmas = pfam.getOrElse(geneId, "_")
      GenomeRow(0, geneId, "Camellia_lanceoleosa", chr, start, end, strand, nrName, nrDes, swissName, swissDes, go, cog, ko, kegg, pfmas)
    }.toSeq

    //row.foreach(println)
    genomeDao.insertGenome(row).toAwait

    println("success")
  }

  def insertColeifera = {
    val path = "H:\\南农油茶数据库\\new\\Coleifera"

    val annos = s"$path/annotation.table.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      val geneid = line.head.replace("T", "G")
      geneid -> line
    }.toMap

    val pfam = s"$path/anno.pfam.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      line.head.replace("T", "G") -> line(3)
    }.toMap


    val row = s"$path/Coleifera.gff".readLines.filter(!_.startsWith("#")).map(_.split("\t")).filter(_(2) == "gene").map { ele =>
      val geneId = ele.last.split(";").find(_.startsWith("ID=")).get.drop(3)
      val chr = ele.head
      val start = ele(3).toInt
      val end = ele(4).toInt
      val strand = ele(6)
      val anno = annos(geneId)
      val nrName = anno(2)
      val nrDes = anno(3)

      val swissName = anno(5)
      val swissDes = anno(6)

      val go = anno(8)
      val cog = anno(9)
      val ko = anno(11)
      val kegg = anno(12)
      val pfmas = pfam.getOrElse(geneId, "_")
      GenomeRow(0, geneId, "Coleifera", chr, start, end, strand, nrName, nrDes, swissName, swissDes, go, cog, ko, kegg, pfmas)
    }.toSeq

    //  row.foreach(println)
    genomeDao.insertGenome(row).toAwait

    println("success")
  }


  def addGenome = {
    val xxx = "H:\\南农油茶数据库\\南荣油茶基因组数据\\南荣基因组\\DataS1-8\\DataS8 new transcripts sequences and annotation\\Data S5 new transcripts sequences and annotation\\Transcriptome_annotated_transcripts/gene.gff"

    val gff = xxx.readLines.filter(_.contains("geneID")).map { l =>
      val lines = l.split("\t")
      val len = lines(4).toInt - lines(3).toInt
      val geneid = lines.last.split(";").find(_.startsWith("geneID")).get.drop(7)
      (geneid, len, lines)
    }.groupBy(_._1).map { x =>
      x._1.split('.').last.toInt -> x._2.maxBy(_._2)._3.mkString("\t")
    }.toSeq.sortBy(_._1).map(_._2)

    val annos = "H:\\南农油茶数据库\\南荣油茶基因组数据\\南荣基因组\\DataS1-8\\DataS8 new transcripts sequences and annotation\\Data S5 new transcripts sequences and annotation\\Transcriptome_annotated_transcripts/newGene.Annot.xls".readLines.
      tail.map(_.split("\t")).map { x =>
      x.head -> x.tail
    }.toMap

    val row = gff.map { x =>
      val ele = x.split("\t")
      val geneId = ele.last.split(";").find(_.startsWith("geneID=")).get.drop(7)
      val chr = ele.head
      val start = ele(3).toInt
      val end = ele(4).toInt
      val strand = ele(6)

      val anno = annos.getOrElse(geneId, Array("_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"))

      val ko = anno(5).split(";").distinct.head
      val go = Array(anno(6), anno(7), anno(8)).map(_.split("//").head).distinct.filter(_ != "-").mkString(";")

      GenomeRow(0, geneId, "2", chr, start, end, strand, "_", "_", "_", "_", go, "_", ko, "_", "_")

    }
    genomeDao.insertGenome(row).toAwait

  }

  def addGenome2 = {
    val path = "H:\\南农油茶数据库\\2024_6_12"
    println(path)

    val annos = s"$path/annotation.table.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      val geneid = line.head
      geneid -> line
    }.toMap

    val pfam = s"$path/anno.pfam.xls".readLines.tail.map { x =>
      val line = x.split("\t")
      line.head-> line(3)
    }.toMap


    val row = s"$path/GWHERAW00000000.gff".readLines.filter(!_.startsWith("#")).filter(_.trim!="").map(_.split("\t")).filter(_(2) == "gene").map { ele =>
      val geneId = ele.last.split(";").find(_.startsWith("Accession=")).get.drop(10)
      val chr = ele.head
      val start = ele(3).toInt
      val end = ele(4).toInt
      val strand = ele(6)
      val anno = annos(geneId)
      val nrName = anno(2)
      val nrDes = anno(3)

      val swissName = anno(5)
      val swissDes = anno(6)

      val go = anno(8)
      val cog = anno(9)
      val ko = anno(11)
      val kegg = anno(12)
      val pfmas = pfam.getOrElse(geneId, "_")
      GenomeRow(0, geneId, "Camellia_crapnelliana", chr, start, end, strand, nrName, nrDes, swissName, swissDes, go, cog, ko, kegg, pfmas)
    }.toSeq

    //  row.foreach(println)
    genomeDao.insertGenome(row).toAwait

    println("success")
  }


  def geteQTL = {
    val path = "H:\\南农油茶数据库\\南荣油茶基因组数据\\南荣基因组\\DataS1-8\\DataS6 eQTL results of C oleifera population\\qtls"

    genomeDao.getAllInfo.toAwait.groupBy(_.chr).map { x =>
      val chr = s"${path}/${x._1}.txt".toFile
      if (chr.exists()) {

        val chrFile = s"${path}/${x._1}.txt".readLines.map(_.split("\t")).map { y =>
          y(2).toInt -> y.mkString("\t")
        }
        x._2.foreach { y =>
          val start = y.start
          val end = y.end
          val lines = chrFile.filter(z => z._1 >= start && z._1 <= end).map(_._2)
          if (lines.nonEmpty) {
            FileUtils.writeLines(s"H:\\南农油茶数据库\\南荣油茶基因组数据\\南荣基因组\\DataS1-8\\DataS6 eQTL results of C oleifera population\\/eqtl/${y.id}.txt".toFile, lines.asJava)
          }
        }
        println(x._1)
      }

    }

  }

  def insertBlockTable = {
    val genome = genomeDao.getAllInfo.toAwait
    val block = "H:\\南农油茶数据库\\mcscanx\\block.txt".readLines.map(_.split("\t")).map(m => m.head -> m.last)
    val bMap1 = block.groupBy(_._1).map(b => b._1 -> b._2.map(_._2))
    val bMap2 = block.groupBy(_._2).map(b => b._1 -> b._2.map(_._1))
    val row = genome.map { y =>
      val blocks = bMap1.getOrElse(y.geneid, mutable.Buffer("")) ++ bMap2.getOrElse(y.geneid, mutable.Buffer(""))
      (y.id, y.geneid, blocks.distinct.filter(_ != "").mkString(";"))
    }.filter(_._3 != "").map { q =>
      BlockRow(q._1, q._2, q._3)
    }
    blockDao.insert(row).toAwait
  }

  def insertBlockRange = {
    val genome = genomeDao.getAllInfo.toAwait.map(x => x.geneid -> (x.chr, x.start.toInt, x.end.toInt, x.species)).toMap

    "H:\\南农油茶数据库\\mcscanx\\collinearity".listFile.foreach { x =>
      val row = x.readFileToString.split("## Alignment").tail.zipWithIndex.map { case (y, i) =>
        val lines = y.trim.split("\n")

        val head = lines.head.split(" ")

        val score = head.find(_.startsWith("score")).get.drop(6)
        val eValue = head.find(_.startsWith("e_value")).get.drop(8)
        val n = head.find(_.startsWith("N=")).get.drop(2)

        val name = x.getName.split('.').init.mkString(".").split("_vs_")
        val name1 = name.head
        val name2 = name.last

        val genes = lines.tail.map(_.split("\t")).map(z => z(1) -> z(2))


        val g = genes.map(_._1).map { z => genome(z) }
        val gChr = g.map(_._1).head
        val gStart = g.map(_._2).min
        val gEnd = g.map(_._3).max
        val gS = g.head._4

        val r = genes.map(_._2).map { z => genome(z) }
        val rChr = r.map(_._1).head
        val rStart = r.map(_._2).min
        val rEnd = r.map(_._3).max
        val rS = r.head._4

        val gs1 = gS
        val rs1 = rS

        BlockrangeRow(0, gs1 + "_vs_" + rs1 + "_" + (i + 1), gChr, gS, gStart, gEnd, gEnd - gStart, rChr, rS, rStart, rEnd, rEnd - rStart, score, eValue, n)
      }
      blockRangeDao.insert(row).toAwait
    }

  }

  def getBlockRange = {
    val genome = genomeDao.getAllInfo.toAwait.map(x => x.geneid -> (x.chr, x.start.toInt, x.end.toInt, x.species)).toMap

    val len = s"${Global.path}/blockLen".listFile.map { x =>
      if(x.getName =="genome.fa.fai"){
        "CON" -> x.readLines.map(_.split("\t")).zipWithIndex.map(y => (y._1.head -> (y._1.last, y._2))).toMap
      }else{
        val name = x.getName.dropRight(4)
        name -> x.readLines.map(_.split("\t")).zipWithIndex.map(y => (y._1.head -> (y._1.last, y._2))).toMap
      }

    }.toMap

    "H:\\南农油茶数据库\\mcscanx\\collinearity".listFile.foreach { x =>
      val name = x.getName.split('.').init.mkString(".")

      val row = x.readFileToString.split("## Alignment").tail.zipWithIndex.map { case (y, i) =>
        val lines = y.trim.split("\n")
        val genes = lines.tail.map(_.split("\t")).map { z => z(1) -> z(2) }
        val g = genes.map(_._1).map { z => genome(z) }
        val gChr = g.map(_._1).head

        val gS = g.head._4

        val r = genes.map(_._2).map { z => genome(z) }
        val rChr = r.map(_._1).head

        val rS = r.head._4

        val gs1 = gS
        val rs1 = rS

        val s1 = len(gs1)
        val s2 = len(rs1)

        if (s1.contains(gChr) && s2.contains(rChr)) {
          val n1 = s1(gChr)
          val len1 = n1._1.toDouble
          val num1 = n1._2.toDouble

          val n2 = s2(rChr)
          val len2 = n2._1.toDouble
          val num2 = n2._2.toDouble

          val datas = genes.map { z =>
            val g1 = genome(z._1)
            val g2 = genome(z._2)
            val pos1 = (g1._3.toDouble + Math.abs((g1._3.toDouble - g1._2.toDouble) / 2)) / len1 + num1
            val pos2 = (g2._3.toDouble + Math.abs((g2._3.toDouble - g2._2.toDouble) / 2)) / len2 + num2
            (pos1, pos2,  z._1 + "(" + g1._1 +":" + g1._2 + "-" + g1._3 + ")", z._2+ "(" + g2._1  + ":" + g2._2 + "-" + g2._3+ ")")
          }

          val json = Json.obj("name" -> (gs1 + "_vs_" + rs1 + "_" + (i + 1)), "data" -> datas.map(x => (x._1, x._2)))

          val pos = datas.map { z =>
            (z._1 + "-" + z._2) -> (z._3 + "\t" + z._4)
          }.toMap

          (json, pos)
        }else{
          (Json.obj("" -> ""),Map("false" -> "false"))
        }
      }.filter(_._2.head._1 != "false")

      val js = Json.toJson(row.map(_._1))
      val poss = Json.toJson(row.flatMap(_._2).toMap)
      FileUtils.writeStringToFile(s"I:/camellia_oleifera_abel_db/config/synteny/${name}.json".toFile,  js.toString   )
      FileUtils.writeStringToFile(s"I:/camellia_oleifera_abel_db/config/synteny/${name}.pos".toFile,  poss.toString   )

    }

  }


}
